<template>
  <view
    :id="id"
    class="tn-rate"
    :class="[rateClass]"
    @touchmove.stop.prevent="handleTouchMove"
  >
    <!-- item -->
    <view
      v-for="(item, index) in rateData"
      :key="index"
      :id="`${itemId}_${index}`"
      class="tn-rate__item"
      :class="[itemClass(index + 1)]"
      :style="itemStyle(index + 1)"
      @tap.stop="handleItemClick"
    >
      <!-- 普通icon -->
      <tn-icon :icon="icon(index + 1)"></tn-icon>

      <!-- 半选项 -->
      <view v-if="showHalfItem(index + 1)" class="half-item" :class="[halfItemClass(index + 1)]" :style="halfItemStyle(index + 1)">
        <tn-icon :icon="halfIcon(index + 1)"></tn-icon>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { rateProps, rateEmits, RateCustomData } from '../../../types/tn-rate'
import { computed, CSSProperties, getCurrentInstance, ComponentInternalInstance, onMounted, ref, nextTick, watch } from 'vue'
import { ColorInfo, TuniaoSizes, TuniaoSize } from '../../../types/tuniao-ui'
import useComponentColor from '../../../libs/hooks/useComponentColor'
import { handleValueWithUnit, uuid, getRectInfo } from '../../../utils'
import TnIcon from '../../tn-icon/src/Icon.vue'
import { UPDATE_MODEL_EVENT } from '../../../constants'

const props = defineProps(rateProps)
const emits = defineEmits(rateEmits)

// 评分item数据
const rateData = computed<Required<RateCustomData>[]>(() => {
  let data: Required<RateCustomData>[] = []
  for (let i = 0; i < props.max; i++) {
    const customIndex = props.customData.findIndex(item => item.index === i)
    let rate: Required<RateCustomData> = {
      index: i,
      activeIcon: props.activeIcon,
      inactiveIcon: props.inactiveIcon,
      activeColor: props.activeColor || '',
      inactiveColor: props.inactiveColor || ''
    }
    rate = Object.assign(rate, customIndex !== -1 ? props.customData[customIndex] : {})
    data.push(rate)
  }
  return data
})

// 颜色解析
const { splitColorInfo } = useComponentColor()
// 选中时的颜色信息
const activeColor = computed<(index: number) => ColorInfo>(() => {
  return (index: number) => {
    return splitColorInfo('color', rateData.value[index].activeColor)
  }
})
// 未选中时的颜色信息
const inactiveColor = computed<(index: number) => ColorInfo>(() => {
  return (index: number) => {
    return splitColorInfo('color', rateData.value[index].inactiveColor)
  }
})

// 图标信息
const icon = computed<(index: number) => string>(() => {
  return (index: number) => {
    return index <= Math.floor(props.modelValue) ? rateData.value[index - 1].activeIcon : rateData.value[index - 1].inactiveIcon
  }
})
const halfIcon = computed<(index: number) => string>(() => {
  return (index: number) => {
    return rateData.value[index - 1].activeIcon
  }
})

// item样式
const itemStyle = computed<(index: number) => CSSProperties>(() => {
  return (index: number) => {
    let style: CSSProperties = {}
    if (props.size && !TuniaoSizes.includes(props.size as TuniaoSize)) style.fontSize = handleValueWithUnit(props.size)
    if (props.gutter) style.margin = `0rpx calc(${handleValueWithUnit(props.gutter)} / 2)`
    if (index <= Math.floor(props.modelValue)) {
      const _activeColor: ColorInfo = activeColor.value(index - 1)
      if (_activeColor.type === 'style') style.color = _activeColor.value
    } else {
      const _inactiveColor: ColorInfo = inactiveColor.value(index - 1)
      if (_inactiveColor.type === 'style') style.color = _inactiveColor.value
    }
    return style
  }
})
// halfItem样式
const halfItemStyle = computed<(index: number) => CSSProperties>(() => {
  return (index: number) => {
    let style: CSSProperties = {}
    const _activeColor: ColorInfo = activeColor.value(index - 1)
    if (_activeColor.type === 'style') style.color = _activeColor.value
    return style
  }
})

// rate对应的类
const rateClass = computed<string>(() => {
  let clazz: string[] = []
  if (props.size && TuniaoSizes.includes(props.size as TuniaoSize)) clazz.push(`tn-rate--${props.size}`)
  if (props.disabled) clazz.push('disabled')
  return clazz.join(' ')
})
// item对应的类
const itemClass = computed<(index: number) => string>(() => {
  return (index: number) => {
    let clazz: string[] = []
    if (index <= Math.floor(props.modelValue)) {
      clazz.push('select')
      const _activeColor: ColorInfo = activeColor.value(index - 1)
      if (_activeColor.type === 'class') clazz.push(_activeColor.value)
    } else {
      const _inactiveColor: ColorInfo = inactiveColor.value(index - 1)
      if (_inactiveColor.type === 'class') clazz.push(_inactiveColor.value)
    }
    return clazz.join(' ')
  }
})
// halfItem对应的类
const halfItemClass = computed<(index: number) => string>(() => {
  return (index: number) => {
    let clazz: string[] = []
    const _activeColor: ColorInfo = activeColor.value(index - 1)
    if (_activeColor.type === 'class') clazz.push(_activeColor.value)
    return clazz.join(' ')
  }
})

// 判断是否显示半个信息
const showHalfItem = computed<(index: number) => boolean>(() => {
  return (index: number) => {
    // 判断传入的值是否带0.5
    if (props.half && props.modelValue % 1 === 0.5) {
      return index === Math.ceil(props.modelValue)
    }
    return false
  }
})

const instance = getCurrentInstance() as ComponentInternalInstance
// 容器id
const id = uuid()
const itemId = uuid()
// 容器相关信息
let rateLeft: number = 0
let rateItemInfo: {
  left: number,
  middleLeft: number,
  right: number
}[] = []

// 获取主容器信息
let getRectCount: number = 0
const getRateRect = async () => {
  const rect = await getRectInfo(`#${id}`, instance)
  if (!rect) {
    if (getRectCount > 10) {
      return console.error('[tn-rate]初始化Rate容器失败')
    }
    setTimeout(() => {
      getRateRect()
      getRectCount++
    }, 50);
    return
  }

  getRectCount = 0
  rateLeft = rect.left || 0
  getRateItemRect()
}
// 获取item信息
const getRateItemRect = () => {
  const query = uni.createSelectorQuery().in(instance)
  for (let i = 0; i < rateData.value.length; i++) {
    query.select(`#${itemId}_${i}`).boundingClientRect(() => {})
  }
  query.exec((res: UniApp.NodeInfo[]) => {
    if (!res || res.length === 0) {
      if (getRectCount > 10) {
        return console.error('[tn-rate]初始化RateItem失败')
      }
      setTimeout(() => {
        getRateItemRect()
        getRectCount++
      }, 50);
      return
    }
    rateItemInfo = []
    res.map((item: UniApp.NodeInfo) => {
      rateItemInfo.push({
        left: (item.left || 0),
        middleLeft: (item.left || 0) + ((item.width as number) / 2),
        right: (item.right || 0)
      })
    })
  })
}
// 处理滑动事件
const handleTouchMove = (e: TouchEvent) => {
  if(props.disabled) return
  const distance = e.changedTouches[0].pageX
  // 处理0星
  if (distance <= rateLeft) {
    setSelectValue(0)
    return
  }

  // 当前触摸位置对应的item位置
  const { index, halfIndex } = handleTouchPositionWithitemIndex(distance)
  if (index === -1) return
  setSelectValue((props.half && index === halfIndex) ? index + 0.5 : index + 1)
}

// 处理item点击事件
const handleItemClick = (e: TouchEvent) => {
  if(props.disabled) return
  const touchPointX = e.changedTouches[0].pageX
  const { index, halfIndex } = handleTouchPositionWithitemIndex(touchPointX)
  if (index === -1) return

  let value: number = (props.half && index === halfIndex) ? index + 0.5 : index + 1
  // 如果点击了两次第一个item则是取消选择, 变为0
  if (props.modelValue === 0.5 && props.half && index === 0 && halfIndex === 0) value = 0
  if (props.modelValue === 1 && index === 0 && halfIndex === -1) value = 0
  setSelectValue(value)
}

// 处理位置与item之间的index
const handleTouchPositionWithitemIndex = (position: number): { index: number, halfIndex: number} => {
  // 当前点击位置对应的item位置
  const index = rateItemInfo.findIndex((item) => {
    return item.left < position && item.right > position
  })
  // 半item的位置
  let halfIndex = -1
  if (props.half) {
    halfIndex = rateItemInfo.findIndex((item) => {
      return item.left < position && item.middleLeft > position
    })
  }
  return { index, halfIndex }
}

// 设置选中的值
const setSelectValue = (value: number) => {
  if (props?.min !== undefined && value < props.min) value = props.min
  emits(UPDATE_MODEL_EVENT, value)
  emits('change', value)
}

// 设置为最小值
watch(() => props.min, (val: number, oVal: number | undefined) => {
  if (props?.modelValue !== undefined && val > props.modelValue) {
    setSelectValue(val)
  }
}, {
  immediate: true
})
onMounted(() => {
  nextTick(() => {
    setTimeout(() => {
      getRateRect()
    }, 50);
  })
})
</script>

<script lang="ts">
export default {
  options: {
    // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
    virtualHost: true
  }
}
</script>

<style lang="scss" scoped>
@import '../../../theme.scss';

.tn-rate {
  position: relative;
  display: flex;
  align-items: center;
  width: fit-content;

  &.disabled {
    opacity: 0.3;
  }

  &__item {
    position: relative;
    box-sizing: border-box;
    font-size: 34rpx;
    color: #AAAAAA;
    line-height: 1;
    margin: 0 calc(8rpx / 2);

    &.select {
      color: $tn-main-color;
    }
    .half-item {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
      width: 50%;
      overflow: hidden;
      color: $tn-main-color;
    }
  }

  /* 内置size start */
  &--sm {
    .tn-rate__item {
      font-size: 28rpx;
    }
  }
  &--lg {
    .tn-rate__item {
      font-size: 40rpx;
    }
  }
  &--xl {
    .tn-rate__item {
      font-size: 48rpx;
    }
  }
  /* 内置size end */
}
</style>
